#include "SimpleMediaFile.h"


using namespace Scenic::Renderer;


static ResourcePool simpleMediaFilePool;


#define REGISTER_FILTERGRAPH

HRESULT AddToROT(IUnknown *pUnkGraph);
void RemoveFromROT(void);

SimpleMediaFile::SimpleMediaFile(const char *filename, Image *image)
: Resource(&simpleMediaFilePool)
{
	printf("SimpleMediaFile::SimpleMediaFile(\"%s\")\n", filename);
	this->filename = strdup(filename);
	this->image = image;
	imageRenderer = 0;
	initRenderer();
}

SimpleMediaFile::~SimpleMediaFile()
{
	release();
}

SimpleMediaFile *SimpleMediaFile::get(int id)
{
	return (SimpleMediaFile*)simpleMediaFilePool.get(id);
}

bool SimpleMediaFile::initRenderer()
{
    HRESULT hr = S_OK;
    CComPtr<IBaseFilter>    pFSrc;          // Source Filter
    CComPtr<IPin>           pFSrcPinOut;    // Source Filter Output Pin   
    
    CoInitialize (NULL);
	
    // Create the filter graph
    if (FAILED(graphBuilder.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC)))
	{
		printf("Failed to create the filter graph\n");
        return false;
	}
	
#ifdef REGISTER_FILTERGRAPH
    // Register the graph in the Running Object Table (for debug purposes)
    AddToROT(graphBuilder);
#endif
    
    // Create the Texture Renderer object
    imageRenderer = new ImageRenderer(image);
    
    // Get a pointer to the IBaseFilter on the TextureRenderer, add it to graph
    renderer = imageRenderer;
    if (FAILED(hr = graphBuilder->AddFilter(renderer, L"IMAGERENDERER")))
    {
        printf("Could not add renderer filter to graph!  hr=0x%x\n", hr);
        return false;
    }
    
	USES_CONVERSION;
	
    WCHAR *wFileName = A2W(filename);
	
    // Add the source filter to the graph.
    hr = graphBuilder->AddSourceFilter (wFileName, L"SOURCE", &pFSrc);
    
    // If the media file was not found, inform the user.
    if (hr == VFW_E_NOT_FOUND)
    {
        printf("Could not add source filter to graph!  (hr==VFW_E_NOT_FOUND)\n");
        return false;
    }
    else if(FAILED(hr))
    {
        printf("Could not add source filter to graph!  hr=0x%x", hr);
        return false;
    }
	
    if (FAILED(hr = pFSrc->FindPin(L"Output", &pFSrcPinOut)))
    {
        printf("Could not find output pin!  hr=0x%x", hr);
        return false;
    }
	
#ifdef NO_AUDIO_RENDERER
	
    // If no audio component is desired, directly connect the two video pins
    // instead of allowing the Filter Graph Manager to render all pins.
	
    CComPtr<IPin> pFTRPinIn;      // Texture Renderer Input Pin
	
    // Find the source's output pin and the renderer's input pin
    if (FAILED(hr = pFTR->FindPin(L"In", &pFTRPinIn)))
    {
        printf("Could not find input pin!  hr=0x%x", hr);
        return false;
    }
	
    // Connect these two filters
    if (FAILED(hr = graphBuilder->Connect(pFSrcPinOut, pFTRPinIn)))
    {
        printf("Could not connect pins!  hr=0x%x", hr);
        return false;
    }
	
#else
	
    // Render the source filter's output pin.  The Filter Graph Manager
    // will connect the video stream to the loaded CTextureRenderer
    // and will load and connect an audio renderer (if needed).
	
    if (FAILED(hr = graphBuilder->Render(pFSrcPinOut)))
    {
        printf("Could not render source output pin!  hr=0x%x", hr);
        return false;
    }
	
#endif
	
    // Get the graph's media control, event & position interfaces
    graphBuilder.QueryInterface(&mediaControl);
    graphBuilder.QueryInterface(&mediaPosition);
    graphBuilder.QueryInterface(&mediaEvent);
    
    // Start the graph running;
    if (FAILED(hr = mediaControl->Run()))
    {
        printf("Could not run the DirectShow graph!  hr=0x%x", hr);
        return false;
    }
	
	printf("Video renderer intialized.\n");
    return true;
}

void SimpleMediaFile::release()
{
	printf("SimpleMediaFile::release\n");
#ifdef REGISTER_FILTERGRAPH
    // Pull graph from Running Object Table (Debug)
    RemoveFromROT();
#endif
	
    // Shut down the graph
    if (!(!mediaControl)) mediaControl->Stop();
	
    if (!(!mediaControl)) mediaControl.Release();
    if (!(!mediaEvent)) mediaEvent.Release();
    if (!(!mediaPosition)) mediaPosition.Release();
    if (!(!graphBuilder)) graphBuilder.Release();
    if (!(!renderer)) renderer.Release();

	releaseReference();
}

void SimpleMediaFile::play()
{
	mediaControl->Run();
}

void SimpleMediaFile::pause()
{
	mediaControl->Pause();
}

double SimpleMediaFile::getCurrentPosition()
{
	REFTIME time;

	mediaPosition->get_CurrentPosition(&time);
	return time;
}

void SimpleMediaFile::setCurrentPosition(double position)
{
	mediaPosition->put_CurrentPosition((REFTIME)position);
}

double SimpleMediaFile::getLength()
{
	REFTIME time;

	mediaPosition->get_Duration(&time);
	return time;
}


#ifdef REGISTER_FILTERGRAPH

//-----------------------------------------------------------------------------
// Running Object Table functions: Used to debug. By registering the graph
// in the running object table, GraphEdit is able to connect to the running
// graph. This code should be removed before the application is shipped in
// order to avoid third parties from spying on your graph.
//-----------------------------------------------------------------------------
DWORD dwROTReg = 0xfedcba98;

HRESULT AddToROT(IUnknown *pUnkGraph) 
{
    IMoniker * pmk;
    IRunningObjectTable *pROT;
    if (FAILED(GetRunningObjectTable(0, &pROT))) {
        return E_FAIL;
    }
	
    WCHAR wsz[256];
    wsprintfW(wsz, L"FilterGraph %08x  pid %08x\0", (DWORD_PTR) 0, GetCurrentProcessId());
	
    HRESULT hr = CreateItemMoniker(L"!", wsz, &pmk);
    if (SUCCEEDED(hr)) 
    {
        // Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference
        // to the object.  Using this flag will cause the object to remain
        // registered until it is explicitly revoked with the Revoke() method.
        //
        // Not using this flag means that if GraphEdit remotely connects
        // to this graph and then GraphEdit exits, this object registration 
        // will be deleted, causing future attempts by GraphEdit to fail until
        // this application is restarted or until the graph is registered again.
        hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph, 
			pmk, &dwROTReg);
        pmk->Release();
    }
	
    pROT->Release();
    return hr;
}

void RemoveFromROT(void)
{
    IRunningObjectTable *pirot=0;
	
    if (SUCCEEDED(GetRunningObjectTable(0, &pirot))) 
    {
        pirot->Revoke(dwROTReg);
        pirot->Release();
    }
}

#endif
